package template;

/* import table */
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;

import logist.simulation.Vehicle;
import logist.agent.Agent;
import logist.behavior.DeliberativeBehavior;
import logist.plan.Action;
import logist.plan.Plan;
import logist.task.Task;
import logist.task.TaskDistribution;
import logist.task.TaskSet;
import logist.topology.Topology;
import logist.topology.Topology.City;

/**
 * An optimal planner for one vehicle.
 */
@SuppressWarnings("unused")
public class DeliberativeAgent implements DeliberativeBehavior {

	enum Algorithm { BFS, ASTAR }

	/* Environment */
	Topology topology;
	TaskDistribution td;

	/* the properties of the agent */
	Agent agent;
	int capacity;

	Long totalSortTime = new Long(0);

	/* the planning class */
	Algorithm algorithm;

	@Override
	public void setup(Topology topology, TaskDistribution td, Agent agent) {
		this.topology = topology;
		this.td = td;
		this.agent = agent;

		// initialize the planner
		int capacity = agent.vehicles().get(0).capacity();
		String algorithmName = agent.readProperty("algorithm", String.class, "ASTAR");

		if (algorithmName.equals("A-star")) {
			algorithmName = new String("ASTAR");
		}

		// Throws IllegalArgumentException if algorithm is unknown
		algorithm = Algorithm.valueOf(algorithmName.toUpperCase());

		// ...
	}


	@Override
	public Plan plan(Vehicle vehicle, TaskSet tasks) {

		Plan plan;
		Long startTime = System.currentTimeMillis();
		// Compute the plan withPlan the selected algorithm.
		State.stateList = new ArrayList<State>();
		switch (algorithm) {
		case ASTAR:
			// ...
			plan = aStarPlan(vehicle, tasks);
			break;
		case BFS:
			// ...
			plan = bfsPlan(vehicle, tasks);
			break;
		default:
			throw new AssertionError("Should not happen.");
		}
		
		Long endTime = System.currentTimeMillis();

		System.out.println("sort time is " + totalSortTime + "milliseconds");
		System.out.println("Totle time is " + (endTime - startTime) + "milliseconds");
		return plan;
	}


	private Plan bfsPlan(Vehicle vehicle, TaskSet tasks) {
		City current = vehicle.getCurrentCity();
		Plan plan = new Plan(current);
		List<State> Q = new ArrayList<State>();
		System.out.println("taskset size: " + tasks.size());
		State initialState = new State(topology, vehicle, tasks);
		Q.add(initialState);
		while (!Q.isEmpty()) {
			State n = Q.get(0);
			//State.stateList.add(n);
			Q.remove(0);
			if (n.isFinalState()) {
				System.out.println("PATH FOUND");
				System.out.println(n.getListActions().toString());
				System.out.println("Total Cost: " + n.getPastCost());
				ArrayList<Action> listActions = n.getListActions();
				for(Action action: listActions){
					plan.append(action);
				}
				return plan;
			}
			ArrayList<State> S = n.bfsSucc();
//			for(State state : S) {
//				state.printStatus();
//			}
			Q.addAll(S);
//			Collections.sort(Q, new Comparator<State>() {
//				public int compare(State s1, State s2) {
//					return (int)(s1.getPastCost() - s2.getPastCost());
//				}
//			});
		}

		System.out.println("No possible plan found !");

		return plan;
	}

	private Plan aStarPlan(Vehicle vehicle, TaskSet tasks) {
		City current = vehicle.getCurrentCity();
		Plan plan = new Plan(current);
		ArrayList<State> Q = new ArrayList<State>();
		System.out.println("taskset size: " + tasks.size());
		State initialState = new State(topology, vehicle, tasks);
		Q.add(initialState);
		while (!Q.isEmpty()) {
			State n = Q.get(0);
			//State.stateList.add(n);
			Q.remove(0);
			//n.debug();
			if (n.isFinalState()) {
				System.out.println("PATH FOUND");
				System.out.println(n.getListActions().toString());
				System.out.println("Total Cost: " + n.getPastCost());
				ArrayList<Action> listActions = n.getListActions();
				for(Action action: listActions){
					plan.append(action);
				}   
				return plan;
			}
			ArrayList<State> S = n.aStarSucc(Q);
			Q.addAll(S);
			Long startTime = System.currentTimeMillis();
			Collections.sort(Q);
			
//			System.out.println("+++++++++++++++++++++++++++++++++++++++++");
//			for (State state: Q) {
//				state.printStatus();
//			}
//			
//			
//			System.out.println("+++++++++++++++++++++++++++++++++++++++++");
//			System.out.println();
//			System.out.println();
			
			Long endTime = System.currentTimeMillis();
			totalSortTime += endTime-startTime;
		}
		return plan;
	}

	private Plan naivePlan(Vehicle vehicle, TaskSet tasks) {
		City current = vehicle.getCurrentCity();
		Plan plan = new Plan(current);

		for (Task task : tasks) {
			// move: current city => pickup location
			for (City city : current.pathTo(task.pickupCity))
				plan.appendMove(city);


			plan.appendPickup(task);

			// move: pickup location => delivery location
			for (City city : task.path())
				plan.appendMove(city);

			plan.appendDelivery(task);

			// set current city
			current = task.deliveryCity;
		}
		return plan;
	}

	@Override
	public void planCancelled(TaskSet carriedTasks) {

		if (!carriedTasks.isEmpty()) {
			// This cannot happen for this simple agent, but typically
			// you will need to consider the carriedTasks when the next
			// plan is computed.
		}
	}
}
